home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1997 January / Macworld (1997-01).dmg / Shareware World / Utilities / Data & Time Management / MacCalendar / Src / MacCalendar.c < prev    next >
C/C++ Source or Header  |  1994-10-20  |  22KB  |  749 lines

  1. /*                                    MacCalendar.c                                    */
  2. /*
  3.  * MacCalendar.c
  4.  * Copyright © 1993-94 Apple Computer Inc. All rights reserved.
  5.  * Based on the Status Bar Sample.c by Steve Christensen.
  6.  *
  7.  *    File Type            sdev
  8.  *    File Creator        SCAL    -- registered with DTS
  9.  *    Resource Type        sdev
  10.  *    Resource ID            0
  11.  *    Resource Attributes    purgeable
  12.  *
  13.  * Other options (MetroWerks, Think C 7.0):
  14.  *    Set Require Prototypes, Check Pointer Types, All other warnings.
  15.  *    Do not set trigraph recognition.
  16.  *    Enable Apple extensions.
  17.  * MetroWerks link/project options:
  18.  *    Link single segment
  19.  *    Set Project type "Code Segment", Standard Header.
  20.  */
  21. #ifndef  SystemSevenOrLater
  22. #define  SystemSevenOrLater    1
  23. #endif
  24.  
  25. #include <GestaltEqu.h>
  26. #include <Fonts.h>
  27. #include <Memory.h>
  28. #include <Menus.h>
  29. #include <Quickdraw.h>
  30. #include <Resources.h>
  31. #include <ToolUtils.h>
  32. #include <Types.h>
  33. #include <Windows.h>
  34. #include <Icons.h>
  35. #ifdef MPW /* Defined in ETO 15 headers */
  36. #include <ControlStrip.h>
  37. #else
  38. #include "ControlStrip.h"
  39. #endif
  40. #include "MacCalendar.h"
  41.  
  42. /*
  43.  * TEMP until universal headers are available
  44.  */
  45. #if 0 
  46. #if defined(powerc) || defined (__powerc)
  47. #pragma options align=mac68k
  48. #endif
  49. struct QDGlobals {
  50.     char                        privates[76];
  51.     long                        randSeed;
  52.     BitMap                        screenBits;
  53.     Cursor                        arrow;
  54.     Pattern                        dkGray;
  55.     Pattern                        ltGray;
  56.     Pattern                        gray;
  57.     Pattern                        black;
  58.     Pattern                        white;
  59.     GrafPtr                        thePort;
  60. };
  61. #if defined(powerc) || defined(__powerc)
  62. #pragma options align=reset
  63. #endif
  64. typedef struct QDGlobals QDGlobals;
  65. #endif /* MPW */
  66.  
  67. /*
  68.  * Metrowerks uses A4 to reference globals. The A4-setup code was copied from the
  69.  * WDEF.c sample included in the Metrowerks DR3 distribution. MPW and Think C
  70.  * use PC-relative addressing in a single-segment code module.
  71.  */
  72. #ifdef __MWERKS__
  73. #include "A4Stuff.h"    //    also included in <MacHeaders>
  74. #include "SetupA4.h"    //    required to handle callback functions
  75. #endif
  76.  
  77. /*
  78.  * Define the patterns as C-strings so they can be addressed as constants
  79.  * within the program.
  80.  */
  81. #define kWhitePattern        ((ConstPatternParam) "\000\000\000\000\000\000\000\000")
  82. #define kBlackPattern        ((ConstPatternParam) "\377\377\377\377\377\377\377\377")
  83. #define pstrcpy(dst, src)    (BlockMove((src), (dst), (src)[0] + 1))
  84. #define width(rect)            ((rect).right - (rect).left)
  85. #define height(rect)        ((rect).bottom - (rect).top)
  86. /*
  87.  * MyGetQDGlobals can be used to point to the current (A5) QuickDraw globals.
  88.  */
  89. #define MyGetQDGlobals() \
  90.  ((QDGlobals *) (*((long *) SetCurrentA5()) - (sizeof (QDGlobals) - sizeof (GrafPtr))))
  91.     
  92.  
  93. /*
  94.  * This record defines the information we need to draw the calendar. It is initialized
  95.  * when we are called with the sdevInitModule message, and passed to and from the
  96.  * Status Bar manager.
  97.  */
  98. typedef struct GlobalRecord {
  99.     Handle            iconSuite;                    /* Status bar icon                    */
  100.     Handle            textStrings;                /* Balloon help string etc.            */
  101.     StringHandle    dateStringHandle;            /* The date string preference        */
  102.     PicHandle        rightArrowPicture;            /* Popup arrow                        */
  103.     short            fontNumber;                    /* Preference: display font         */
  104.     short            fontSize;                    /* Preference: display font size    */
  105.     short            firstDayOfWeek;                /* Sunday == 1                        */
  106. } GlobalRecord, *GlobalPtr, **GlobalHandle;
  107. /*
  108.  * We access the global information through a macro. For example, GLOBAL.iconSuite is
  109.  * the icon suite handle in the global record.
  110.  */
  111. #define GLOBAL            (*globalPtr)
  112. #define PicFrame(what)    ((**GLOBAL.what).picFrame)    /* The picture frame rectangle    */
  113.  
  114. /*
  115.  * Define the local/global prototypes.
  116.  */
  117. pascal long                    main(
  118.         unsigned long            message,
  119.         GlobalHandle            globalHandle,
  120.         const Rect                *statusRect,
  121.         GrafPtr                    statusPort
  122.     );
  123. long                        CtlStripInitialize(void);
  124. void                        CtlStripCleanUp(
  125.         GlobalHandle            globalHandle
  126.     );
  127. long                        CtlStripPeriodicTickle(
  128.         GlobalPtr                globalPtr,
  129.         const Rect                *statusRect
  130.     );
  131. long                        CtlStripMouseClick(
  132.         GlobalPtr                globalPtr,
  133.         const Rect                *statusRect
  134.     );
  135. long                        CtlStripDrawStatusIcon(
  136.         GlobalPtr                globalPtr,
  137.         const Rect                *statusRect
  138.     );
  139. OSErr                        CtlStripSavePreferences(
  140.         GlobalPtr                globalPtr
  141.     );
  142.  
  143.  
  144. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  145.  * main
  146.  *
  147.  * This "main" program is called by the Control Strip manager.
  148.  */
  149. pascal long
  150. main(
  151.         unsigned long            message,
  152.         GlobalHandle            globalHandle,
  153.         const Rect                *statusRect,
  154.         GrafPtr                    statusPort
  155.     )
  156. {
  157. #ifdef __MWERKS__
  158.         long                    oldA4 = SetCurrentA4();
  159. #endif
  160.         short                    savedState;
  161.         register GlobalPtr        globalPtr;
  162.         long                    result;
  163.         Str255                    helpString;
  164.  
  165.         if (0) {                                    /* Unused variable                */
  166.             statusPort;
  167.         }
  168.         if (((long) globalHandle) != 0) {
  169.             /*
  170.              * We have already allocated the global record. Save its lock state, lock
  171.              * the handle, and get the global pointer (so we can write GLOBAL.something)
  172.              */
  173.             savedState = HGetState((Handle) globalHandle);
  174.             HLock((Handle) globalHandle);
  175.             globalPtr = *globalHandle;
  176.         }
  177.         result = 0;                                    /* Unknown message result        */
  178.         switch (message) {
  179.         case sdevInitModule:                        /* Initialize the module        */
  180.             /*
  181.              * Initialization always sets globalHandle to NULL to avoid the HSetState
  182.              * at the exit routine. If CtlStripInitialize succeeds, it sets the result
  183.              * to the global parameter.
  184.              */
  185.             globalHandle = NULL;
  186.             result = CtlStripInitialize();            /* Do the initialization and    */
  187.             break;                                    /* Return global or error code    */
  188.         case sdevCloseModule:                        /* Clean up before closing        */
  189.             CtlStripCleanUp(globalHandle);
  190.             globalHandle = NULL;
  191.             break;
  192.         case sdevFeatures:                            /* Return feature bits            */
  193.             result = (  (1<<sdevWantMouseClicks)    /* We handle mouse down            */
  194.                       | (1<<sdevDontAutoTrack)        /* We track the mouse, too        */
  195.                       | (1<<sdevHasCustomHelp)        /* Custom help string            */
  196.                     );
  197.             break;
  198.         case sdevGetDisplayWidth:                    /* Return display width            */
  199.             result = kIconWidth    + width(PicFrame(rightArrowPicture));
  200.             break;
  201.         case sdevPeriodicTickle:                    /* Nothing else is happening    */
  202.             result = CtlStripPeriodicTickle(globalPtr, statusRect);
  203.             break;
  204.         case sdevDrawStatus:                        /* Draw the status bar info        */
  205.             result = CtlStripDrawStatusIcon(globalPtr, statusRect);
  206.             break;
  207.         case sdevMouseClick:                        /* Status bar click                */
  208.             result = CtlStripMouseClick(globalPtr, statusRect);
  209.             break;
  210.         case sdevSaveSettings:                        /* Save changed settings        */
  211.             result = CtlStripSavePreferences(globalPtr);
  212.             break;
  213.         case sdevShowBalloonHelp:                    /* Display custom balloon help    */
  214.             /*
  215.              * We don't really have a custom help string, but this shows how to do it.
  216.              */
  217.             SBGetDetachedIndString(helpString, GLOBAL.textStrings, kStringHelp);
  218.             SBShowHelpString(statusRect, helpString);
  219.             break;
  220.         }
  221.         if (globalHandle != NULL)                    /* If we have globals allocated    */
  222.             HSetState((Handle) globalHandle, savedState);    /* Restore lock state    */    
  223. #ifdef __MWERKS__
  224.         SetA4(oldA4);
  225. #endif
  226.         return (result);
  227. }
  228.  
  229.  
  230. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  231.  * Initialization
  232.  */
  233. long
  234. CtlStripInitialize(void)
  235. {
  236.         register GlobalHandle    globalHandle;
  237.         register GlobalPtr        globalPtr;
  238.         long                    result;
  239.         Str255                    work;
  240.         SavedSettingsHandle        prefsHandle;
  241.         Handle                    *theIconSuite;
  242.         Boolean                    needSave;
  243.         long                    tempLong;
  244. #define PREF    (**prefsHandle)
  245.  
  246.         needSave = FALSE;
  247.         globalHandle = (GlobalHandle) NewHandleClear(sizeof (GlobalRecord));
  248.         result = noErr;
  249.         if (globalHandle == NULL)
  250.             result = MemError();
  251.         else {
  252.             HLock((Handle) globalHandle);
  253.             globalPtr = *globalHandle;
  254.             /*
  255.              * Load and detach the icon suite
  256.              */
  257.             theIconSuite = &GLOBAL.iconSuite;
  258.             result = SBGetDetachIconSuite(theIconSuite, ICON_StatusBar, svAllSmallData);
  259.         }
  260.         if (result == noErr) {
  261.             GLOBAL.textStrings = GetResource('STR#', STRN_Info);
  262.             result = ResError();
  263.         }
  264.         if (result == noErr) {
  265.             DetachResource(GLOBAL.textStrings);
  266.             GLOBAL.rightArrowPicture = GetPicture(PICT_RightArrow);
  267.             if (GLOBAL.rightArrowPicture == NULL)
  268.                 result = ResError();
  269.         }
  270.         if (result == noErr) {
  271.             DetachResource((Handle) GLOBAL.rightArrowPicture);
  272.             /*
  273.              * Get the saved preferences, if any, and configure the drawing
  274.              * environment. Note that the sample status bar doesn't dispose
  275.              * of the prefsHandle.
  276.              */
  277.             SBGetDetachedIndString(work, GLOBAL.textStrings, kStringPreference);
  278.             result = SBLoadPreferences(work, (Handle *) &prefsHandle);
  279.             if (result == noErr
  280.              && GetHandleSize((Handle) prefsHandle) == sizeof (SavedSettings)
  281.              && PREF.signature == kApplicationCreator
  282.              && PREF.prefVersion == kPrefVersion
  283.              && PREF.firstDayOfWeek >= kFirstIsSunday
  284.              && PREF.firstDayOfWeek <= kFirstIsMonday) {
  285.                  /*
  286.                   * Use the saved preference resource
  287.                   */
  288.                 pstrcpy(work, PREF.fontName);
  289.                 GetFNum(work, &GLOBAL.fontNumber);
  290.                 GLOBAL.fontSize = PREF.fontSize;
  291.                 GLOBAL.firstDayOfWeek = PREF.firstDayOfWeek;
  292.                 pstrcpy(work, PREF.dateString);        /* This must be last            */
  293.             }
  294.             else {
  295.                 /*
  296.                  * Hmm, we don't have any preferences. Build a new preference resource.
  297.                  */
  298.                 result = noErr;
  299.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFontName);
  300.                 GetFNum(work, &GLOBAL.fontNumber);
  301.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFontSize);
  302.                 StringToNum(work, &tempLong);
  303.                 GLOBAL.fontSize = tempLong;
  304.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFirstDayOfWeek);
  305.                 StringToNum(work, &tempLong);
  306.                 GLOBAL.firstDayOfWeek = tempLong;
  307.                 /*
  308.                  * This must be last as we will store work in the dateStringHandle
  309.                  */
  310.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringDayNames);
  311.                 needSave = TRUE;
  312.             }
  313.             GLOBAL.dateStringHandle = NewString(work);
  314.             if (GLOBAL.dateStringHandle == NULL)
  315.                 result = MemError();
  316.         }
  317.         /*
  318.          * We've finished all initialization. If there is an error, exit through
  319.          * CtlStripCleanUp to dispose of handles and other junk. If initialization
  320.          * are successful, unlock the handle and return the handle cast to a long.
  321.          */
  322.         if (result != noErr) {
  323.             CtlStripCleanUp(globalHandle);
  324.             result = 0;
  325.         }
  326.         else {
  327.             if (needSave)
  328.                 (void) CtlStripSavePreferences(globalPtr);
  329.             HUnlock((Handle) globalHandle);
  330.             result = (long) globalHandle;
  331.         }
  332.         return (result);
  333. #undef PREF
  334. }
  335.  
  336.  
  337. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  338.  * Termination
  339.  */
  340. void
  341. CtlStripCleanUp(
  342.         GlobalHandle            globalHandle
  343.     )
  344. {
  345.         register GlobalPtr        globalPtr;
  346.  
  347.         if ((long) globalHandle > 0) {
  348.             HLock((Handle) globalHandle);
  349.             globalPtr = *globalHandle;
  350.             if (GLOBAL.iconSuite != NULL)
  351.                 DisposeIconSuite(GLOBAL.iconSuite, TRUE);
  352.             if (GLOBAL.textStrings != NULL)
  353.                 DisposeHandle(GLOBAL.textStrings);
  354.             if (GLOBAL.rightArrowPicture != NULL)
  355.                 DisposeHandle((Handle) GLOBAL.rightArrowPicture);
  356.             if (GLOBAL.dateStringHandle != NULL)
  357.                 DisposeHandle((Handle) GLOBAL.dateStringHandle);
  358.             DisposeHandle((Handle) globalHandle);
  359.         }
  360. }
  361.  
  362.  
  363. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  364.  * Draw the icon in the status bar.
  365.  */
  366. long
  367. CtlStripDrawStatusIcon(
  368.         GlobalPtr                globalPtr,
  369.         const Rect                *statusRect
  370.     )
  371. {
  372.         Rect                    viewRect;
  373.         short                    arrowHeight;
  374.  
  375.         viewRect = *statusRect;
  376.         viewRect.right = viewRect.left + kIconWidth;
  377.         (void) PlotIconSuite(&viewRect, atNone, ttNone, GLOBAL.iconSuite);
  378.         /*
  379.          * Draw an right-arrow to show that we have a popup menu. Well, we don't
  380.          * actually have a popup menu, but we do pop up a calendar when clicked on.
  381.          */
  382.         arrowHeight = height(PicFrame(rightArrowPicture));
  383.         viewRect.left = viewRect.right;
  384.         viewRect.right += width(PicFrame(rightArrowPicture));
  385.         viewRect.top += ((height(viewRect) - arrowHeight) >> 1);
  386.         viewRect.bottom = viewRect.top + arrowHeight;
  387.         DrawPicture(GLOBAL.rightArrowPicture, &viewRect);
  388.         return (0);
  389. }
  390.  
  391. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  392.  * Position the calendar with respect to the status bar. If there is enough room
  393.  * above, put it above, else put it below. Left and right operate similarly.
  394.  * 1.0d7 - extensive changes by Frederic Misery to prevent the calendar from being
  395.  * displayed across two monitors.
  396.  */
  397. static void
  398. GetDisplayRect(
  399.         const Rect                *statusRect,
  400.         Point                    displaySize,
  401.         Rect                    *windowRect
  402.     )
  403. {
  404.         long                    gestaltResult;
  405.         Rect                    sBarRect;
  406.         Rect                    limitRect;
  407.         GDHandle                statusDevice;
  408.  
  409. #define kCtlStripFrame    4        /* The frame above/below the icon itself            */
  410.         sBarRect = *statusRect;
  411.         LocalToGlobal((Point *) &sBarRect.top);
  412.         LocalToGlobal((Point *) &sBarRect.bottom);
  413.         statusDevice = NULL;
  414.         /*
  415.          * Find the graphics device that contains the largest part of the statusRect.
  416.          */
  417.         if (Gestalt(gestaltQuickdrawVersion, &gestaltResult) == noErr
  418.          && gestaltResult >= gestalt8BitQD) {
  419.             unsigned long        deviceArea;
  420.             unsigned long        maxArea;
  421.             Rect                tempRect;
  422.             GDHandle            theDevice;
  423.  
  424.             maxArea = 0;
  425.             for (theDevice = GetDeviceList();
  426.                     theDevice != NULL;
  427.                     theDevice = GetNextDevice(theDevice)) {
  428.                 if (SectRect(&(**theDevice).gdRect, &sBarRect, &tempRect)) {
  429.                     deviceArea = width(tempRect) * height(tempRect);
  430.                     if (deviceArea > maxArea) {
  431.                         maxArea = deviceArea;
  432.                         statusDevice = theDevice;
  433.                     }
  434.                 }
  435.             }
  436.         }
  437.         limitRect = (statusDevice != NULL)
  438.                     ? (**statusDevice).gdRect
  439.                     : MyGetQDGlobals()->screenBits.bounds;
  440.         InsetRect(&limitRect, 1, kCtlStripFrame);
  441.         if (statusDevice == NULL || statusDevice == GetMainDevice())
  442.             limitRect.top += GetMBarHeight();
  443.         if (sBarRect.top - displaySize.v > limitRect.top) {
  444.             /*
  445.              * The calendar is displayed above the status bar.
  446.              * This is the preferred location.
  447.              */
  448.             windowRect->bottom = sBarRect.top - kCtlStripFrame;
  449.             windowRect->top = windowRect->bottom - displaySize.v;
  450.         }
  451.         else {
  452.             /*
  453.              * The calendar is displayed below the status bar.
  454.              */
  455.             windowRect->top = sBarRect.bottom + kCtlStripFrame;
  456.             windowRect->bottom = windowRect->top + displaySize.v;
  457.         }
  458.         /*
  459.          * Display the calendar to the right of the calendar icon, right justified
  460.          * against the display edge, if necessary.
  461.          */
  462.         windowRect->left = sBarRect.left - kCtlStripFrame;
  463.         windowRect->right = windowRect->left + displaySize.h;
  464.         if (windowRect->right > limitRect.right)
  465.             OffsetRect(windowRect, limitRect.right - windowRect->right, 0);
  466. }
  467.  
  468. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  469.  * Build the triangular "next month" and "previous month" buttons.
  470.  */
  471. static void
  472. MakeTriangularButtons(
  473.         const Rect                *monthRect,
  474.         PolyHandle                *leftButton,
  475.         PolyHandle                *rightButton,
  476.         Rect                    *leftButtonRect,
  477.         Rect                    *rightButtonRect
  478.     )
  479. {
  480.         FontInfo                fontInfo;
  481.         short                    buttonSize;
  482.         short                    halfSize;
  483.         Rect                    bothButtonRect;
  484.  
  485.         GetFontInfo(&fontInfo);
  486.         buttonSize = (fontInfo.ascent & ~1);        /* Round down to even value        */
  487.         halfSize = buttonSize / 2;
  488.         bothButtonRect = *monthRect;
  489.         bothButtonRect.bottom -= (1 + fontInfo.leading);
  490.         bothButtonRect.top = bothButtonRect.bottom - buttonSize;
  491. #define kButtonSeparation    4                        /* 1.0d3, was 2                    */
  492.         bothButtonRect.left =
  493.             (width(*monthRect) >> 1) - buttonSize - kButtonSeparation;
  494.         bothButtonRect.right =
  495.             (width(*monthRect) >> 1) + buttonSize + kButtonSeparation;
  496.         *leftButtonRect = bothButtonRect;
  497. /* 1.0d4 +    */
  498.         leftButtonRect->right = leftButtonRect->left + halfSize;
  499.         *rightButtonRect = bothButtonRect;
  500.         rightButtonRect->left = rightButtonRect->right - halfSize;
  501.         *leftButton = OpenPoly();
  502.             MoveTo(halfSize, 0);
  503.             LineTo(halfSize, buttonSize);
  504.             LineTo(0, halfSize);
  505.             LineTo(halfSize, 0);
  506.         ClosePoly();
  507.         OffsetPoly(*leftButton, leftButtonRect->left, leftButtonRect->top);
  508.         *rightButton = OpenPoly();
  509.             MoveTo(0, 0);
  510.             LineTo(halfSize, halfSize);
  511.             LineTo(0, buttonSize);
  512.             LineTo(0, 0);
  513.         ClosePoly();
  514.         OffsetPoly(*rightButton, rightButtonRect->left, rightButtonRect->top);
  515. /* 1.0d4 -    */
  516. }
  517.  
  518. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  519.  * Now that we've done setup, do the actual mouse tracking. If the mouse hits the
  520.  * right button, advance the month and draw it. If it hits the left button, draw
  521.  * the previous month.
  522.  */
  523. static void
  524. DrawCalendarAndTrackMouse(
  525.         GlobalPtr                globalPtr,
  526.         WindowPtr                windowPtr,
  527.         const Rect                *monthRect
  528.     )
  529. {
  530.         Rect                    leftButtonRect;
  531.         Rect                    rightButtonRect;
  532.         PolyHandle                leftButton;
  533.         PolyHandle                rightButton;
  534.         unsigned long            nowSeconds;
  535.         DateTimeRec                now;
  536.         Point                    mousePt;
  537.         short                    thisYear;
  538.         short                    thisMonth;
  539.         typedef enum {
  540.             inNoButton = 0,
  541.             inLeftButton = 1,
  542.             inRightButton = 2
  543.         } WhichButton;
  544.         WhichButton                inButton;
  545.         WhichButton                wasInButton;
  546.         unsigned long            nextMonthTick;
  547.         Boolean                    redrawButtons;
  548.         Str255                    dateString;
  549.  
  550.         FrameRect(monthRect);
  551.         MakeTriangularButtons(
  552.             monthRect,
  553.             &leftButton,
  554.             &rightButton,
  555.             &leftButtonRect,
  556.             &rightButtonRect
  557.         );
  558.         pstrcpy(dateString, *GLOBAL.dateStringHandle);
  559.         GetDateTime(&nowSeconds);
  560.         Secs2Date(nowSeconds, &now);
  561.         inButton = wasInButton = inNoButton;
  562.         thisYear = thisMonth = 0;
  563.         InitCursor();
  564.         while (WaitMouseUp()) {
  565.             if (thisYear != now.year || thisMonth != now.month) {
  566.                 /*
  567.                  * Draw the new month. Also make sure the buttons are drawn.
  568.                  */
  569.                 redrawButtons = TRUE;
  570.                 EraseRect(&windowPtr->portRect);
  571.                 FrameRect(monthRect);
  572.                 switch (wasInButton) {
  573.                 case inLeftButton:
  574.                     FillPoly(leftButton, kWhitePattern);
  575.                     break;
  576.                 case inRightButton:
  577.                     FillPoly(rightButton, kWhitePattern);
  578.                     break;
  579.                 }
  580.                 FramePoly(leftButton);
  581.                 FramePoly(rightButton);
  582.                 DrawCalendar(
  583.                     now.year,
  584.                     now.month,
  585.                     GLOBAL.firstDayOfWeek,                /* Sunday                    */
  586.                     dateString,
  587.                     &windowPtr->portRect,
  588.                     GLOBAL.fontNumber,
  589.                     GLOBAL.fontSize
  590.                 );
  591.                 thisYear = now.year;
  592.                 thisMonth = now.month;
  593.             }                                            /* If drawing new month        */
  594.             /*
  595.              * Get the mouse and track it while it is in one of our buttons
  596.              */
  597.             GetMouse(&mousePt);
  598.             if (PtInRect(mousePt, &leftButtonRect))
  599.                 inButton = inLeftButton;
  600.             else if (PtInRect(mousePt, &rightButtonRect))
  601.                 inButton = inRightButton;
  602.             else {
  603.                 inButton = inNoButton;
  604.             }
  605.             if (redrawButtons || inButton != wasInButton) {
  606.                 switch (wasInButton) {
  607.                 case inLeftButton:    FillPoly(leftButton, kWhitePattern);    break;
  608.                 case inRightButton:    FillPoly(rightButton, kWhitePattern);    break;
  609.                 }
  610.                 switch (inButton) {
  611.                 case inLeftButton:    FillPoly(leftButton, kBlackPattern);    break;
  612.                 case inRightButton:    FillPoly(rightButton, kBlackPattern);    break;
  613.                 }
  614.                 FramePoly(leftButton);
  615.                 FramePoly(rightButton);
  616.                 if (inButton != wasInButton && inButton != inNoButton)
  617.                     nextMonthTick = 0;                    /* Force new month drawing    */
  618.                 redrawButtons = FALSE;
  619.                 wasInButton = inButton;
  620.             }                                            /* If button click change    */
  621.             if (inButton != inNoButton && TickCount() > nextMonthTick) {
  622.                 /*
  623.                  * The user has clicked in a button, or has held the mouse
  624.                  * down in a button for one second. Draw the appropriate month.
  625.                  */
  626.                 nextMonthTick = TickCount() + 60;
  627.                 switch (inButton) {
  628.                 case inLeftButton:
  629.                     if (--now.month <= 0) {                /* Previous month or year    */
  630.                         now.month = 12;
  631.                         --now.year;
  632.                     }
  633.                     break;
  634.                 case inRightButton:
  635.                     if (++now.month > 12) {                /* Next month or year        */
  636.                         now.month = 1;
  637.                         ++now.year;
  638.                     }
  639.                     break;
  640.                 }                                        /* Which button was clicked    */
  641.             }                                            /* Moving to a new month    */
  642.         }                                                /* Loop while mouse down    */
  643.         KillPoly(leftButton);
  644.         KillPoly(rightButton);
  645. }
  646.  
  647. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  648.  * Click in the status bar icon
  649.  */
  650. long
  651. CtlStripMouseClick(
  652.         GlobalPtr                globalPtr,
  653.         const Rect                *statusRect
  654.     )
  655. {
  656.         Rect                    windowRect;
  657.         WindowPtr                windowPtr;
  658.         GrafPtr                    savePort;
  659.         Point                    displaySize;
  660.         Rect                    monthRect;
  661.  
  662.         displaySize = GetCalendarDisplaySize(GLOBAL.fontNumber, GLOBAL.fontSize);
  663.         displaySize.h += 2;
  664.         displaySize.v += 2;
  665.         GetDisplayRect(statusRect, displaySize, &windowRect);
  666.         windowPtr = NewWindow(
  667.                     NULL,
  668.                     &windowRect,
  669.                     "\p",
  670.                     TRUE,
  671.                     plainDBox,
  672.                     (WindowPtr) -1L,
  673.                     FALSE,                                /* No go-away box            */
  674.                     0                                    /* No refCon                */
  675.                 );
  676.         if (windowPtr != NULL) {
  677.             GetPort(&savePort);
  678.             SetPort(windowPtr);
  679.             GetCalendarMonthRect(
  680.                 GLOBAL.fontNumber,
  681.                 GLOBAL.fontSize,
  682.                 &windowPtr->portRect,
  683.                 &monthRect
  684.             );
  685.             if (StillDown()) {
  686.                 /*
  687.                  * Design the two triangular buttons that will be displayed on the
  688.                  * bottom line of the calendar and create the polygons. Then draw
  689.                  * the calendar and track the mouse while it's held down.
  690.                  */
  691.                 DrawCalendarAndTrackMouse(
  692.                     globalPtr,
  693.                     windowPtr,
  694.                     &monthRect
  695.                 );
  696.             }
  697.             SetPort(savePort);
  698.             DisposeWindow(windowPtr);
  699.         }
  700.         return (0);
  701. }
  702.  
  703.  
  704. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  705.  * Save current status for restarts.
  706.  */
  707. OSErr
  708. CtlStripSavePreferences(
  709.         GlobalPtr                globalPtr
  710.     )
  711. {
  712.         OSErr                    status;
  713.         SavedSettingsHandle        prefsHandle;
  714.         Str255                    work;
  715. #define PREF    (**prefsHandle)
  716.  
  717.         prefsHandle = (SavedSettingsHandle) NewHandleClear(sizeof (SavedSettings));
  718.         status = MemError();
  719.         if (status == noErr) {
  720.             HLock((Handle) prefsHandle);
  721.             PREF.signature = kApplicationCreator;
  722.             PREF.prefVersion = kPrefVersion;
  723.             GetFontName(GLOBAL.fontNumber, PREF.fontName);
  724.             PREF.fontSize = GLOBAL.fontSize;
  725.             PREF.firstDayOfWeek = GLOBAL.firstDayOfWeek;        /* * 1.0d7            */
  726.             pstrcpy(PREF.dateString, *GLOBAL.dateStringHandle);
  727.             SBGetDetachedIndString(work, GLOBAL.textStrings, kStringPreference);
  728.             status = SBSavePreferences(work, (Handle) prefsHandle);
  729.             DisposeHandle((Handle) prefsHandle);
  730.         }
  731.         return(status);
  732. }
  733.  
  734. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  735.  * We don't do anything interesting for periodic tickles.
  736.  */
  737. long
  738. CtlStripPeriodicTickle(
  739.         GlobalPtr                globalPtr,
  740.         const Rect                *statusRect
  741.     )
  742. {
  743.         if (0) {                /* Unused variables            */
  744.             globalPtr;
  745.             statusRect;
  746.         }
  747.         return (0);
  748. }
  749.